package com.kakarote.hrm.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.IdcardUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.BigExcelWriter;
import cn.hutool.poi.excel.ExcelUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.kakarote.common.log.entity.OperateLogEntity;
import com.kakarote.common.log.enums.ApplyEnum;
import com.kakarote.common.log.enums.BehaviorEnum;
import com.kakarote.common.log.enums.OperateObjectEnum;
import com.kakarote.common.log.enums.OperateTypeEnum;
import com.kakarote.common.log.service.OperateLogSaveService;
import com.kakarote.core.common.MultipartFileUtil;
import com.kakarote.core.common.cache.AdminCacheKey;
import com.kakarote.core.common.enums.SystemCodeEnum;
import com.kakarote.core.entity.UploadEntity;
import com.kakarote.core.exception.CrmException;
import com.kakarote.core.feign.admin.entity.AdminMessage;
import com.kakarote.core.feign.admin.entity.AdminMessageEnum;
import com.kakarote.core.feign.admin.service.AdminFileService;
import com.kakarote.core.feign.admin.service.AdminMessageService;
import com.kakarote.core.feign.admin.service.AdminService;
import com.kakarote.core.redis.Redis;
import com.kakarote.core.servlet.ApplicationContextHolder;
import com.kakarote.core.utils.UserUtil;
import com.kakarote.hrm.common.EmployeeHolder;
import com.kakarote.hrm.common.HrmCodeEnum;
import com.kakarote.hrm.constant.*;
import com.kakarote.hrm.entity.BO.UploadExcelBO;
import com.kakarote.hrm.entity.PO.*;
import com.kakarote.hrm.entity.VO.EmployeeInfo;
import com.kakarote.hrm.service.*;
import com.kakarote.hrm.service.actionrecord.impl.EmployeeActionRecordServiceImpl;
import com.kakarote.hrm.utils.EmployeeCacheUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

@Service
@Slf4j
public class HrmUploadExcelServiceImpl implements HrmUploadExcelService {

    private final static Logger logger = LoggerFactory.getLogger(HrmUploadExcelServiceImpl.class);

    public static final String DEPT = "dept";
    public static final String ORG = "org";
    @Autowired
    private AdminService adminService;

    @Autowired
    private AdminFileService fileService;

    @Autowired
    private Redis redis;

    @Autowired
    private OperateLogSaveService logSaveService;

    @Autowired
    private IHrmEmployeeService employeeService;

    @Autowired
    private IHrmEmployeeDataChangeRecordService employeeDataChangeRecordService;
    /**
     * 上传excel可查询时间
     */
    protected static final int UPLOAD_EXCEL_EXIST_TIME = 7200;

    @Resource
    private ThreadPoolTaskExecutor hrmThreadPoolExecutor;

    private static final int TWO = 2;

    private static final String DEPT_ID = "dept_id";

    private static final String ORG_ID = "org_id";

    protected int sheetNum = 0;

    /**
     * 导入excel
     */
    @Override
    public Long uploadExcel(MultipartFile file, UploadExcelBO uploadExcelBO) {
        String filePath = getFilePath(file);
        uploadExcelBO.setFilePath(filePath);
        AdminMessage adminMessage = new AdminMessage();
        adminMessage.setCreateUser(UserUtil.getUserId());
        adminMessage.setCreateTime(DateUtil.formatDateTime(new Date()));
        adminMessage.setRecipientUser(UserUtil.getUserId());
        adminMessage.setLabel(8);
        adminMessage.setType(AdminMessageEnum.HRM_EMPLOYEE_IMPORT.getType());
        Long messageId = adminService.saveOrUpdateMessage(adminMessage).getData();
        uploadExcelBO.setMessageId(messageId);
        uploadExcelBO.setUserInfo(UserUtil.getUser());
        redis.setex(AdminCacheKey.UPLOAD_EXCEL_MESSAGE_PREFIX + messageId.toString(), UPLOAD_EXCEL_EXIST_TIME, 0);
        UploadService uploadService = new EmployeeUploadService(uploadExcelBO);

        UploadEntity uploadEntity = fileService.uploadTempFile(file, IdUtil.simpleUUID()).getData();
        redis.setex(AdminCacheKey.UPLOAD_EXCEL_MESSAGE_PREFIX + "file:" + uploadEntity.getFileId(), 604800, uploadEntity.getPath());

        JSONObject operationObj = new JSONObject();
        operationObj.put("uploadData", uploadEntity.getFileId());
        operationObj.put("name", "员工管理（人资）");
        operationObj.put("uploadType", uploadExcelBO.getRepeatHandling());
        uploadService.operateLogEntity.setOperationObj(operationObj.toString());
        uploadService.operateLogEntity.setApply(ApplyEnum.HRM.getType());
        uploadService.operateLogEntity.setBehavior(BehaviorEnum.EXCEL_IMPORT.getType());
        uploadService.operateLogEntity.setType(OperateTypeEnum.IMPORT.getType());
        uploadService.operateLogEntity.setModule(OperateObjectEnum.HRM_EMPLOYEE.getType());
        uploadService.operateLogEntity.setCreateUserId(UserUtil.getUserId());
        uploadService.operateLogEntity.setCreateTime(DateUtil.formatDateTime(new Date()));

        hrmThreadPoolExecutor.execute(uploadService);

        return messageId;
    }

    @Override
    public Long uploadAttendanceExcel(MultipartFile file, UploadExcelBO uploadExcelBO) {
        String filePath = getFilePath(file);
        uploadExcelBO.setFilePath(filePath);
        AdminMessage adminMessage = new AdminMessage();
        adminMessage.setCreateUser(UserUtil.getUserId());
        adminMessage.setCreateTime(DateUtil.formatDateTime(new Date()));
        adminMessage.setRecipientUser(UserUtil.getUserId());
        adminMessage.setLabel(8);
        adminMessage.setType(AdminMessageEnum.HRM_EMPLOYEE_ATTENDANCE_IMPORT.getType());
        Long messageId = adminService.saveOrUpdateMessage(adminMessage).getData();
        uploadExcelBO.setMessageId(messageId);
        uploadExcelBO.setUserInfo(UserUtil.getUser());
        redis.setex(AdminCacheKey.UPLOAD_EXCEL_MESSAGE_PREFIX + messageId.toString(), UPLOAD_EXCEL_EXIST_TIME, 0);
        UploadAttendanceService uploadService = new EmployeeAttendanceUploadService(uploadExcelBO);
        hrmThreadPoolExecutor.execute(uploadService);
        return messageId;
    }

    /**
     * 获取临时文件路径,用完记得删除
     *
     * @param file file
     * @return path
     */
    private String getFilePath(MultipartFile file) {
        String dirPath = FileUtil.getTmpDirPath();
        try {
            InputStream inputStream = file.getInputStream();
            File fromStream = FileUtil.writeFromStream(inputStream, dirPath + "/" + IdUtil.simpleUUID() + file.getOriginalFilename());
            return fromStream.getAbsolutePath();
        } catch (IOException e) {
            throw new CrmException(SystemCodeEnum.SYSTEM_UPLOAD_FILE_ERROR);
        }
    }

    public abstract class UploadService implements Runnable {

        protected List<List<Object>> errorList = new CopyOnWriteArrayList<>();

        protected List<HrmEmployeeField> fieldList = new CopyOnWriteArrayList<>();

        protected List<HrmEmployeeField> fixedFieldList = new CopyOnWriteArrayList<>();

        protected List<HrmEmployeeField> uniqueList = new CopyOnWriteArrayList<>();

        protected volatile AtomicInteger num = new AtomicInteger(0);

        protected volatile AtomicInteger updateNum = new AtomicInteger(0);

        protected List<Integer> isNullList = new CopyOnWriteArrayList<>();

        protected volatile boolean templateErr = false;

        protected volatile JSONObject kv = new JSONObject();

        protected OperateLogEntity.Entity.Builder operateLogEntity = OperateLogEntity.Entity.newBuilder();

        public abstract void importExcel();

        public abstract UploadExcelBO getUploadExcelBO();

        protected Map<String, List<List<Object>>> errorExcelMap = new HashMap<>();

        @Override
        public abstract void run();


        /**
         * 查询导入顺序
         *
         * @param rowList 行数据
         * @author zhangzhiwei
         */
        protected void queryExcelHead(List<Object> rowList) {
            HashMap<String, String> nameMap = new HashMap<>();
            HashMap<String, Integer> isNullMap = new HashMap<>();
            fieldList.forEach(filed -> {
                if (Objects.equals(1, filed.getIsFixed())) {
                    fixedFieldList.add(filed);
                }
                if (Objects.equals(1, filed.getIsUnique())) {
                    uniqueList.add(filed);
                }
                boolean isNull = Objects.equals(1, filed.getIsNull());
                String name = filed.getName() + (isNull ? "(*)" : "");
                if (filed.getType().equals(FieldTypeEnum.DATE.getValue())) {
                    name += "-例:[2020-10-01]";
                }
                nameMap.put(name, filed.getFieldName());
                isNullMap.put(name, filed.getIsNull());
            });
            List<String> nameList = new ArrayList<>(nameMap.keySet());
            if (nameList.size() != rowList.size() || !nameList.containsAll(rowList)) {
                templateErr = true;
            } else {
                for (int i = 0; i < rowList.size(); i++) {
                    kv.put(nameMap.get(rowList.get(i).toString()), i);
                    if (Objects.equals(1, isNullMap.get(rowList.get(i).toString()))) {
                        isNullList.add(i);
                    }
                }
            }
            rowList.add(0, "错误原因");
            errorList.add(rowList);
        }
    }


    public class EmployeeUploadService extends UploadService {
        private Long createEmployeeId = EmployeeHolder.getEmployeeInfo().getEmployeeId();
        private Long createDeptId = EmployeeHolder.getEmployeeInfo().getDeptId();
        private String createEmployeeName = EmployeeHolder.getEmployeeInfo().getEmployeeName();

        private UploadExcelBO uploadExcelBO;

        private EmployeeUploadService(UploadExcelBO uploadExcelBO) {
            this.uploadExcelBO = uploadExcelBO;
        }

        @Override
        public void importExcel() {
            try{
                //获取权限下的部门ID
                sheetNum = 0;
                logger.error("importExcel begin ------");
                Workbook workbook = WorkbookFactory.create(Files.newInputStream(new File(uploadExcelBO.getFilePath()).toPath()));
                for(int i=0; i<workbook.getNumberOfSheets(); i++){
                    for(LabelGroupEnum labelGroupEnum : LabelGroupEnum.values()){
                        templateErr = false;
                        kv = new JSONObject();
                        isNullList = new CopyOnWriteArrayList<>();
                        if(workbook.getSheetName(i).equals(labelGroupEnum.getName())){
                            if(LabelGroupEnum.PERSONAL.getValue() == labelGroupEnum.getValue()){
                                logger.error("importExcelForEmployee ------");
                                importExcelForEmployee(0, labelGroupEnum.getValue());
                            }else{
                                logger.error("importExcelForSheet {}------", labelGroupEnum.getName());
                                importExcelForSheet(i, labelGroupEnum.getValue());
                            }
                            writeErrLog(labelGroupEnum.getName());
                            sheetNum ++;
                        }
                    }
                }
            }catch(Exception ex){
                log.error("importExcel Err!", ex);
            }
        }

        /**
         * 基于sheet导入员工信息
         * @param numberOfSheet
         * @param labelGroup
         */
        public void importExcelForSheet(int numberOfSheet, int labelGroup) {
            ExcelUtil.readBySax(uploadExcelBO.getFilePath(), numberOfSheet, (int sheetIndex, long rowIndex, List<Object> rowList) -> {
                int num = this.num.incrementAndGet();
                redis.setex(AdminCacheKey.UPLOAD_EXCEL_MESSAGE_PREFIX + getUploadExcelBO().getMessageId().toString(), UPLOAD_EXCEL_EXIST_TIME, Math.max(num - 2, 0));
                if (rowList.size() < kv.entrySet().size()) {
                    for (int i = rowList.size() - 1; i < kv.entrySet().size(); i++) {
                        rowList.add(null);
                    }
                }
                if (this.num.get() > 200001) {
                    rowList.add(0, "最多同时导入200000条数据");
                    errorList.add(rowList);
                    return;
                }
                if (rowIndex > 1) {
                    if (templateErr) {
                        rowList.add(0, "请使用最新的模板");
                        errorList.add(rowList);
                    } else {
                        try {
                            for (Integer index : isNullList) {
                                if (ObjectUtil.isEmpty(rowList.get(index))) {
                                    throw new CrmException(HrmCodeEnum.REQUIRED_FIELDS_ARE_NOT_FILLED);
                                }
                            }

                            //加载个人信息模版字段
                            final Long[] unitId = {null};
                            final Long[] unitId2 = {null};
                            fieldList.stream().peek(field -> {
                                String fieldName = field.getFieldName();
                                Object object = rowList.get(kv.getInteger(fieldName));
                                Object value = "";
                                if (field.getType() == FieldTypeEnum.DATE.getValue() && ObjectUtil.isNotEmpty(object)) {
                                    if (object instanceof Long) {
                                        value = org.apache.poi.ss.usermodel.DateUtil.getJavaDate((Long) object);
                                    }else {
                                        String dateStr = String.valueOf(object).trim();
                                        if (dateStr.length() > 12) {
                                            LocalDateTime parse = LocalDateTimeUtil.parse(dateStr, "yyyy-MM-dd HH:mm:ss");
                                            value = DateTimeFormatter.ofPattern("yyyy-MM-dd").format(parse);
                                        } else {
                                            LocalDateTime parse = LocalDateTimeUtil.parse(dateStr, "yyyy-MM-dd");
                                            value = DateTimeFormatter.ofPattern("yyyy-MM-dd").format(parse);
                                        }
                                    }

                                } else if (field.getType() == FieldTypeEnum.NUMBER.getValue()) {
                                    value = Convert.toInt(object);
                                } else if("employee_id".equals(field.getFieldName())){
                                    if (ObjectUtil.isNotEmpty(object)) {
                                        String idNumber = StrUtil.trim(Convert.toStr(object));
                                        if (StrUtil.isNotEmpty(idNumber)) {
//                                            if (!IdcardUtil.isValidCard(idNumber)) {
//                                                throw new CrmException(HrmCodeEnum.IDENTITY_INFORMATION_IS_ILLEGAL, idNumber);
//                                            }
                                        }
                                        EmployeeInfo employeeInfo = ApplicationContextHolder.getBean(IHrmEmployeeService.class).queryEmployeeInfoByIdNumber(idNumber);
                                        if(employeeInfo == null){
                                            //用户信息不存在
                                            logger.error("idNumber:{}", idNumber);
                                            throw new CrmException(HrmCodeEnum.UNABLE_TO_MATCH_HRM_EMPLOYEES, idNumber);
                                        }
                                        value = Convert.toStr(employeeInfo.getEmployeeId());
                                    } else {
                                        throw new CrmException(HrmCodeEnum.IDENTITY_INFORMATION_IS_ILLEGAL);
                                    }
                                } else if (ORG.equals(field.getFieldName())) {
                                    if (ObjectUtil.isNotEmpty(object)) {
                                        //获取机构id
                                        Optional<HrmDept> hrmDeptOpt = ApplicationContextHolder.getBean(IHrmDeptService.class).lambdaQuery().eq(HrmDept::getName, StrUtil.trim(Convert.toStr(object))).oneOpt();
                                        value = hrmDeptOpt.map(HrmDept::getDeptId).orElse(null);
                                        if (ObjectUtil.isEmpty(value)) {
                                            throw new CrmException(500, "机构不存在");
                                        }
                                        unitId[0] = Convert.toLong(value);
                                    } else {
                                        value = null;
                                    }
                                } else if (DEPT.equals(field.getFieldName())) {
                                    if (ObjectUtil.isNotEmpty(object)) {
                                        Optional<HrmDept> hrmDeptOpt = ApplicationContextHolder.getBean(IHrmDeptService.class).lambdaQuery().eq(HrmDept::getParentId, unitId[0]).eq(HrmDept::getName, StrUtil.trim(Convert.toStr(object))).oneOpt();
                                        value = hrmDeptOpt.map(HrmDept::getDeptId).orElse(null);
                                        if (ObjectUtil.isEmpty(value)) {
                                            throw new CrmException(500, "部门不存在");
                                        }
                                    } else {
                                        value = null;
                                    }
                                } else if (ORG_ID.equals(field.getFieldName())) {
                                    if (ObjectUtil.isNotEmpty(object)) {
                                        //获取机构id
                                        Optional<HrmDept> hrmDeptOpt = ApplicationContextHolder.getBean(IHrmDeptService.class).lambdaQuery().eq(HrmDept::getName, StrUtil.trim(Convert.toStr(object))).oneOpt();
                                        value = hrmDeptOpt.map(HrmDept::getDeptId).orElse(null);
                                        if (ObjectUtil.isEmpty(value)) {
                                            throw new CrmException(500, "机构不存在");
                                        }
                                        unitId2[0] = Convert.toLong(value);
                                    } else {
                                        value = null;
                                    }
                                } else if (DEPT_ID.equals(field.getFieldName())) {
                                    if (ObjectUtil.isNotEmpty(object)) {
                                        Optional<HrmDept> hrmDeptOpt = ApplicationContextHolder.getBean(IHrmDeptService.class).lambdaQuery().eq(HrmDept::getParentId, unitId2[0]).eq(HrmDept::getName, StrUtil.trim(Convert.toStr(object))).oneOpt();
                                        value = hrmDeptOpt.map(HrmDept::getDeptId).orElse(null);
                                        if (ObjectUtil.isEmpty(value)) {
                                            throw new CrmException(500, "部门不存在");
                                        }
                                    } else {
                                        value = null;
                                    }
                                } else {
                                    if (ObjectUtil.isNotEmpty(object)) {
                                        value = Convert.toStr(object);
                                    }
                                }
                                field.setValue(value);
                                logger.debug("fieldName:{} value:{} Type:{}", fieldName, value, field.getType());
                            }).collect(Collectors.groupingBy(HrmEmployeeField::getLabelGroup));

                            //基于sheet导入数据
                            importExcelForPublic(labelGroup, fieldList);
                        } catch (CrmException e) {
                            log.error("导入数据异常:", e);
                            rowList.add(0, e.getMsg());
                            errorList.add(rowList);
                        } catch (JSONException e) {
                            log.error("导入数据异常:", e);
                            rowList.add(0, "数据格式异常,请使用模板规定格式");
                            errorList.add(rowList);
                        } catch (Exception ex) {
                            log.error("导入数据异常:", ex);
                            rowList.add(0, ex.getMessage());
                            errorList.add(rowList);
                        }
                    }
                } else if (rowIndex == 1) {
                    fieldList = ApplicationContextHolder.getBean(IHrmEmployeeService.class).downloadExcelFiled().stream()
                            .filter(t -> t.getLabelGroup() == labelGroup||
                                    t.getLabelGroup() == EmployeeLableGroup.LABLE_GROUP_99.getValue())
                            .collect(Collectors.toList());
                    queryExcelHead(rowList);
                } else {
                    errorList.add(rowList);
                }
            });
        }

        /**
         * 导入
         * @param labelGroup
         * @param employeeFields
         */
        public void importExcelForPublic(int labelGroup, List<HrmEmployeeField> employeeFields){
            //固定字段
            JSONObject employeeFix = new JSONObject();
            //动态字段
            List<HrmEmployeeData> employeeDataLocalList = new ArrayList<>();
            //家庭成员动态字段
            List<HrmEmployeeContactsData> employeeContactsDataLocalList = new ArrayList<>();
            employeeFields.forEach(field -> {
                Object value = field.getValue();
                if (field.getType() == FieldTypeEnum.SELECT.getValue() && field.getComponentType() == ComponentType.NO.getValue()) {
                    List<Map<String, String>> list = JSON.parseObject(field.getOptions(), List.class);
                    boolean matchFlag = false;
                    for (Map<String, String> map : list) {
                        if (map.get("name").equals(value)) {
                            value = map.get("value");
                            matchFlag = true;
                            break;
                        }
                    }
                    if( !matchFlag){
                        value = null;
                    }
                }

                if (field.getIsFixed() == IsEnum.YES.getValue()) {
                    employeeFix.put(field.getFieldName(), value);
                } else {
                    HrmEmployeeData hrmEmployeeData = new HrmEmployeeData();
                    hrmEmployeeData.setFieldId(field.getFieldId());
                    hrmEmployeeData.setFieldName(field.getFieldName());
                    hrmEmployeeData.setLabelGroup(field.getLabelGroup());
                    hrmEmployeeData.setName(field.getName());
                    hrmEmployeeData.setFieldValue(String.valueOf(value));
                    hrmEmployeeData.setFieldValueDesc(String.valueOf(value));
                    HrmEmployeeField employee = employeeFields.stream().filter(e -> e.getFieldName().equals("employee_id")).findFirst().get();
                    hrmEmployeeData.setEmployeeId(Long.valueOf(Convert.toStr(employee.getValue())));
                    employeeDataLocalList.add(hrmEmployeeData);
                }

                if (LabelGroupEnum.CONTACT_PERSON.getValue() == labelGroup) {
                    HrmEmployeeContactsData hrmEmployeeContactsData = new HrmEmployeeContactsData();
                    hrmEmployeeContactsData.setFieldId(field.getFieldId());
                    hrmEmployeeContactsData.setLabelGroup(field.getLabelGroup());
                    hrmEmployeeContactsData.setName(field.getName());
                    hrmEmployeeContactsData.setFieldValue(String.valueOf(value));
                    hrmEmployeeContactsData.setFieldValueDesc(String.valueOf(value));
                    employeeContactsDataLocalList.add(hrmEmployeeContactsData);
                }
            });
            //插入到通用表中
            if (CollUtil.isNotEmpty(employeeDataLocalList)) {
                ApplicationContextHolder.getBean(IHrmEmployeeDataService.class).saveOrUpdateBatch(employeeDataLocalList, employeeDataLocalList.size());
            }
            if(LabelGroupEnum.EDUCATIONAL_EXPERIENCE.getValue() == labelGroup){
                //教育经历
                HrmEmployeeEducationExperience hrmEmployeeEducationExperience = employeeFix.toJavaObject(HrmEmployeeEducationExperience.class);
                ApplicationContextHolder.getBean(IHrmEmployeeEducationExperienceService.class).save(hrmEmployeeEducationExperience);
            }else if(LabelGroupEnum.WORK_EXPERIENCE.getValue() == labelGroup){
                //工作经历
                HrmEmployeeWorkExperience hrmEmployeeWorkExperience = employeeFix.toJavaObject(HrmEmployeeWorkExperience.class);
                ApplicationContextHolder.getBean(IHrmEmployeeWorkExperienceService.class).save(hrmEmployeeWorkExperience);
            }else if(LabelGroupEnum.TRAINING_EXPERIENCE.getValue() == labelGroup){
                //培训经历
                HrmEmployeeTrainingExperience hrmEmployeeTrainingExperience = employeeFix.toJavaObject(HrmEmployeeTrainingExperience.class);
                ApplicationContextHolder.getBean(IHrmEmployeeTrainingExperienceService.class).save(hrmEmployeeTrainingExperience);
            }else if(LabelGroupEnum.CONTACT_PERSON.getValue() == labelGroup){
                //联系人
                HrmEmployeeContacts hrmEmployeeContacts = employeeFix.toJavaObject(HrmEmployeeContacts.class);
                ApplicationContextHolder.getBean(IHrmEmployeeContactsService.class).save(hrmEmployeeContacts);
                Long contactsId = hrmEmployeeContacts.getContactsId();
                employeeContactsDataLocalList.forEach(e->{
                    e.setContactsId(contactsId);
                });
                ApplicationContextHolder.getBean(IHrmEmployeeContactsDataService.class).saveBatch(employeeContactsDataLocalList, employeeContactsDataLocalList.size());
            }
//            else if(LabelGroupEnum.SOCIAL.getValue() == labelGroup){
//                //主要社会关系
//                HrmEmployeeSocial hrmEmployeeSocial = employeeFix.toJavaObject(HrmEmployeeSocial.class);
//                ApplicationContextHolder.getBean(IHrmEmployeeSocialService.class).save(hrmEmployeeSocial);
//            }
            else if(LabelGroupEnum.CERTIFICATE.getValue() == labelGroup){
                //职业资格
                HrmEmployeeCertificate certificate = employeeFix.toJavaObject(HrmEmployeeCertificate.class);
                ApplicationContextHolder.getBean(IHrmEmployeePostService.class).save(certificate);
            }else if(LabelGroupEnum.TECHNICAL.getValue() == labelGroup){
                //专业技术职称
                HrmEmployeeTechnical hrmEmployeeTechnical = employeeFix.toJavaObject(HrmEmployeeTechnical.class);
                ApplicationContextHolder.getBean(IHrmEmployeeTechnicalService.class).save(hrmEmployeeTechnical);
            } else if(LabelGroupEnum.SPECIAL_WORKER.getValue() == labelGroup){
                // 专业技术工人证书
                HrmEmployeeSpecialWorker hrmEmployeeSpecialWorker =  employeeFix.toJavaObject(HrmEmployeeSpecialWorker.class);
                ApplicationContextHolder.getBean(IHrmEmployeeSpecialWorkerService.class).save(hrmEmployeeSpecialWorker);
            } else if(LabelGroupEnum.CONCURRENT_POST.getValue() == labelGroup){
                // 兼职信息
                HrmEmployeeConcurrentPost hrmEmployeeConcurrentPost =  employeeFix.toJavaObject(HrmEmployeeConcurrentPost.class);
                ApplicationContextHolder.getBean(IHrmEmployeeConcurrentPostService.class).save(hrmEmployeeConcurrentPost);
            } else if (LabelGroupEnum.POST.getValue() == labelGroup) {
                // 个人信息
                HrmEmployee hrmEmployee = employeeFix.toJavaObject(HrmEmployee.class);
                ApplicationContextHolder.getBean(IHrmEmployeeService.class).saveOrUpdate(hrmEmployee);
            } else {
                //暂不处理
            }
        }

        private void importExcelForEmployee(int sheetNum, int labelGroup) {
            ExcelUtil.readBySax(uploadExcelBO.getFilePath(), sheetNum, (int sheetIndex, long rowIndex, List<Object> rowList) -> {
                int num = this.num.incrementAndGet();
                redis.setex(AdminCacheKey.UPLOAD_EXCEL_MESSAGE_PREFIX + getUploadExcelBO().getMessageId().toString(), UPLOAD_EXCEL_EXIST_TIME, Math.max(num - 2, 0));
                if (rowList.size() < kv.entrySet().size()) {
                    for (int i = rowList.size() - 1; i < kv.entrySet().size(); i++) {
                        rowList.add(null);
                    }
                }
                int maxIndex = 10001;
                if (this.num.get() > maxIndex) {
                    rowList.add(0, "最多同时导入10000条数据");
                    errorList.add(rowList);
                    return;
                }
                if (rowIndex > 1) {
                    if (templateErr) {
                        rowList.add(0, "请使用最新的模板");
                        errorList.add(rowList);
                    } else {
                        try {
                            for (Integer index : isNullList) {
                                if (ObjectUtil.isEmpty(rowList.get(index))) {
                                    throw new CrmException(HrmCodeEnum.REQUIRED_FIELDS_ARE_NOT_FILLED);
                                }
                            }
                            Map<Integer, List<HrmEmployeeField>> labelGroupMap = fieldList.stream().peek(field -> {
                                String fieldName = field.getFieldName();
                                Object object = rowList.get(kv.getInteger(fieldName));
                                Object value = "";
                                if (field.getType() == FieldTypeEnum.DATE.getValue() && ObjectUtil.isNotEmpty(object)) {
                                    if (object instanceof Long) {
                                        value = org.apache.poi.ss.usermodel.DateUtil.getJavaDate((Long) object);
                                    }else {
                                        String dateStr = String.valueOf(object).trim();
                                        if (dateStr.length() > 12) {
                                            LocalDateTime parse = LocalDateTimeUtil.parse(dateStr, "yyyy-MM-dd HH:mm:ss");
                                            value = DateTimeFormatter.ofPattern("yyyy-MM-dd").format(parse);
                                        } else {
                                            LocalDateTime parse = LocalDateTimeUtil.parse(dateStr, "yyyy-MM-dd");
                                            value = DateTimeFormatter.ofPattern("yyyy-MM-dd").format(parse);
                                        }
                                    }

                                } else if (field.getType() == FieldTypeEnum.NUMBER.getValue()) {
                                    value = Convert.toInt(object);
                                } else {
                                    if (ObjectUtil.isNotEmpty(object)) {
                                        value = Convert.toStr(object);
                                    }
                                }
                                field.setValue(value);
                            }).collect(Collectors.groupingBy(HrmEmployeeField::getLabelGroup));
                            //只有个人信息个岗位信息值自定义字段,需要验证字段唯一性
                            List<HrmEmployeeField> employeeFields = labelGroupMap.get(LabelGroupEnum.PERSONAL.getValue());
                            List<HrmEmployeeField> uniqueList = new ArrayList<>();
                            for (HrmEmployeeField employeeField : employeeFields) {
                                if (employeeField.getIsUnique() == IsEnum.YES.getValue()) {
                                    uniqueList.add(employeeField);
                                }
                            }
                            if (uniqueList.size() > 0) {
                                Integer uniqueCount = ApplicationContextHolder.getBean(IHrmEmployeeService.class).queryFieldValueNoDelete(uniqueList);
                                if (uniqueCount > 0) {
                                    if (Objects.equals(TWO, uploadExcelBO.getRepeatHandling())) {
                                        return;
                                    }
                                    if (uniqueCount > 1) {
                                        throw new CrmException(HrmCodeEnum.DATA_IS_DUPLICATED_WITH_MULTIPLE_UNIQUE_FIELDS);
                                    }
                                }
                            }
                            JSONObject employee = new JSONObject();
                            List<HrmEmployeeData> employeeDataList = new ArrayList<>();
                            employeeFields.addAll(labelGroupMap.get(LabelGroupEnum.COMMUNICATION.getValue()));
                            employeeFields.forEach(field -> {
                                Object value = field.getValue();
                                if (field.getType() == FieldTypeEnum.SELECT.getValue() && field.getComponentType() == ComponentType.NO.getValue()) {
                                    List<Map<String, String>> list = JSON.parseObject(field.getOptions(), List.class);
                                    for (Map<String, String> map : list) {
                                        if (map.get("name").equals(value)) {
                                            value = map.get("value");
                                        }
                                    }
                                }
                                if (field.getIsFixed() == IsEnum.YES.getValue()) {
                                    if (field.getType() == FieldTypeEnum.DATE.getValue()) {
                                        if (ObjectUtil.isNotEmpty(value)) {
                                            value = LocalDateTimeUtil.of(DateUtil.parseDate(value.toString())).toLocalDate();
                                        }
                                    }
                                    employee.put(field.getFieldName(), value);
                                } else {
                                    HrmEmployeeData hrmEmployeeData = new HrmEmployeeData();
                                    hrmEmployeeData.setFieldId(field.getFieldId());
                                    hrmEmployeeData.setFieldName(field.getFieldName());
                                    hrmEmployeeData.setLabelGroup(field.getLabelGroup());
                                    hrmEmployeeData.setName(field.getName());
                                    hrmEmployeeData.setFieldValue(String.valueOf(value));
                                    hrmEmployeeData.setFieldValueDesc(String.valueOf(field.getValue()));
                                    employeeDataList.add(hrmEmployeeData);
                                }
                            });
                            for (HrmEmployeeData hrmEmployeeData : employeeDataList) {
                                if (LabelGroupEnum.COMMUNICATION.getValue() == hrmEmployeeData.getLabelGroup()){
                                    hrmEmployeeData.setLabelGroup(LabelGroupEnum.CONTACT_PERSON.getValue());
                                }
                            }
                            HrmEmployee employee1 = employee.toJavaObject(HrmEmployee.class);
                            boolean saveEmployee = true;
                            // 设置出生年月
                            if (StrUtil.isNotBlank(employee1.getIdNumber())) {
                                String idNumber = employee1.getIdNumber();
                                employee1.setIdNumber(idNumber.trim());
//                                if (!IdcardUtil.isValidCard(idNumber)) {
//                                    throw new CrmException(HrmCodeEnum.IDENTITY_INFORMATION_IS_ILLEGAL, idNumber);
//                                }
                                employee1.setDateOfBirth(IdcardUtil.getBirthDate(idNumber).toLocalDateTime().toLocalDate());
                            }

                            // 设置年纪
                            if (ObjectUtil.isNotNull(employee1.getDateOfBirth())) {
                                if (employee1.getDateOfBirth().isBefore(LocalDate.now())) {
                                    employee1.setAge(DateUtil.ageOfNow(LocalDateTimeUtil.format(employee1.getDateOfBirth(), DatePattern.NORM_DATE_PATTERN)));
                                }
                            }

                            if (StrUtil.isNotEmpty(employee1.getIdNumber())) {
                                LambdaQueryChainWrapper<HrmEmployee> wrapper = ApplicationContextHolder.getBean(IHrmEmployeeService.class).lambdaQuery()
                                        .eq(HrmEmployee::getIdNumber, employee1.getIdNumber()).eq(HrmEmployee::getIsDel, 0);
                                boolean count;
                                if (employee1.getEmployeeId() == null) {
                                    count = wrapper.exists();
                                } else {
                                    count = wrapper.ne(HrmEmployee::getEmployeeId, employee1.getEmployeeId()).exists();
                                }

                                if (count) {
                                    // 1:覆盖系统原有数据 2:跳过
                                    logger.error("2 RepeatHandling:{} getEmployeeId:{}", uploadExcelBO.getRepeatHandling(), wrapper.one().getEmployeeId());
                                    if (Objects.equals(2, uploadExcelBO.getRepeatHandling())) {
                                        return;
                                    } else if (Objects.equals(1, uploadExcelBO.getRepeatHandling())) {
                                        //清理掉相关数据
                                        employee1.setEmployeeId(wrapper.one().getEmployeeId());
                                        clearEmployeeInfo(employee1.getEmployeeId());
                                        updateNum.incrementAndGet();
                                    } else{
                                        throw new CrmException(HrmCodeEnum.ID_NUMBER_EXISTS_ERROR, employee1.getMobile());
                                    }
                                }
                            }
                            employee1.setEntryStatus(EmployeeEntryStatusEnum.在职.getValue());
                            employee1.setStatus(EmployeeStatusEnum.OFFICIAL.getValue());
//                            if (employee1.getEmploymentForms().equals(EmploymentFormsEnum.OFFICIAL.getValue())) {
//                                Integer probation = employee1.getProbation();
//                                if (employee1.getBecomeTime() != null) {
//                                    LocalDate dateTime = employee1.getBecomeTime();
//                                    if (dateTime.isAfter(LocalDateTimeUtil.beginOfDay(LocalDateTime.now()).toLocalDate())) {
//                                        employee1.setStatus(EmployeeStatusEnum.TRY_OUT.getValue());
//                                    } else {
//                                        employee1.setStatus(EmployeeStatusEnum.OFFICIAL.getValue());
//                                    }
//                                } else if (ObjectUtil.isNotNull(probation)) {
//                                    if (probation == 0) {
//                                        employee1.setStatus(EmployeeStatusEnum.OFFICIAL.getValue());
//                                        employee1.setBecomeTime(employee1.getEntryTime());
//                                    } else {
//                                        LocalDateTime dateTime = LocalDateTimeUtil.offset(employee1.getEntryTime().atStartOfDay(), probation, ChronoUnit.MONTHS);
//                                        if (dateTime.isAfter(LocalDateTimeUtil.beginOfDay(LocalDateTime.now()))) {
//                                            employee1.setStatus(EmployeeStatusEnum.TRY_OUT.getValue());
//                                        } else {
//                                            employee1.setStatus(EmployeeStatusEnum.OFFICIAL.getValue());
//                                        }
//                                        employee1.setBecomeTime(dateTime.toLocalDate());
//                                    }
//                                } else {
//                                    employee1.setStatus(EmployeeStatusEnum.OFFICIAL.getValue());
//                                }
//                            } else {
//                                employee1.setStatus(EmployeeStatusEnum.INTERNSHIP.getValue());
//                            }
                            employee1.setEmploymentForms(1);
                            ApplicationContextHolder.getBean(IHrmEmployeeService.class).saveOrUpdate(employee1);
                            if (saveEmployee) {
                                ApplicationContextHolder.getBean(EmployeeActionRecordServiceImpl.class).addOrDeleteRecord(HrmActionBehaviorEnum.ADD, LabelGroupEnum.PERSONAL, employee1.getEmployeeId());
                            }
                            employeeDataList.forEach(data -> {
                                data.setEmployeeId(employee1.getEmployeeId());
                            });
                            ApplicationContextHolder.getBean(IHrmEmployeeDataService.class).lambdaUpdate().eq(HrmEmployeeData::getEmployeeId, employee1.getEmployeeId()).remove();
                            ApplicationContextHolder.getBean(IHrmEmployeeDataService.class).saveBatch(employeeDataList, employeeDataList.size());
                            //发送通知
                            AdminMessage adminMessage = new AdminMessage();
                            adminMessage.setCreateUser(UserUtil.getUserId());
                            adminMessage.setCreateTime(DateUtil.formatDateTime(new Date()));
                            adminMessage.setRecipientUser(EmployeeCacheUtil.getUserId(employee1.getEmployeeId()));
                            adminMessage.setLabel(8);
                            adminMessage.setType(AdminMessageEnum.HRM_EMPLOYEE_OPEN.getType());
                            ApplicationContextHolder.getBean(AdminMessageService.class).save(adminMessage);
                        } catch (CrmException e) {
                            log.error("导入数据异常:", e);
                            rowList.add(0, e.getMsg());
                            errorList.add(rowList);
                        } catch (JSONException e) {
                            log.error("导入数据异常:", e);
                            rowList.add(0, "数据格式异常,请使用模板规定格式");
                            errorList.add(rowList);
                        } catch (Exception ex) {
                            log.error("导入数据异常:", ex);
                            rowList.add(0, ex.getMessage());
                            errorList.add(rowList);
                        }
                    }
                } else if (rowIndex == 1) {
                    fieldList = ApplicationContextHolder.getBean(IHrmEmployeeService.class).downloadExcelFiled().stream()
                            .filter(t -> t.getLabelGroup() == labelGroup || t.getLabelGroup() == EmployeeLableGroup.LABLE_GROUP_2.getValue())
                            .collect(Collectors.toList());
                    queryExcelHead(rowList);
                } else {
                    errorList.add(0, rowList);
                }
            });
        }

        @Override
        public UploadExcelBO getUploadExcelBO() {
            return uploadExcelBO;
        }

        @Override
        public void run() {
            boolean exists = redis.exists(AdminCacheKey.UPLOAD_EXCEL_MESSAGE_PREFIX + uploadExcelBO.getMessageId());
            if (!exists) {
                return;
            }
            AdminMessage adminMessage = adminService.getMessageById(getUploadExcelBO().getMessageId()).getData();

            JSONObject info = new JSONObject();
            try {
                UserUtil.setUser(getUploadExcelBO().getUserInfo());
                importExcel();

                adminMessage.setTitle(String.valueOf(num.get() - 2));
                adminMessage.setContent((errorList.size() - 2) + "," + updateNum.get());

                info.put("count", Math.max(num.get() - 2, 0));
                info.put("updateNum", updateNum);
                info.put("errorSize", errorList.size() - 2);

                adminService.saveOrUpdateMessage(adminMessage);
                //更新es员工数据
                employeeService.initEmployeeEsData(new ArrayList<>());
                //保存操作记录
                HrmEmployeeDataChangeRecord record = new HrmEmployeeDataChangeRecord();
                record.setType(EmployeeDataChangeType.EMPLOYEE.getValue());
                record.setSubDeptId(createDeptId);
                record.setSubEmployeeId(createEmployeeId);
                record.setRemark(String.format("员工 %s 通过EXCEL导入了员工信息。", createEmployeeName));
                employeeDataChangeRecordService.addChangeRecord(record);
            } catch (Exception e) {
                log.error("导入出现错误", e);
            } finally {
                redis.del(AdminCacheKey.UPLOAD_EXCEL_MESSAGE_PREFIX + uploadExcelBO.getMessageId());
                UserUtil.removeUser();
                //写入日志文件记录
                writeErrLogForFile();
            }

            operateLogEntity.setOperationInfo(info.toJSONString());
            logSaveService.saveOperateLog(operateLogEntity);
            FileUtil.del(getUploadExcelBO().getFilePath());
        }

        /**
         * 写入错误日志文件
         */
        public void writeErrLogForFile(){
            try{
                int errNum = 0;
                //打开excel
                File file = FileUtil.file("excelData/" + IdUtil.simpleUUID() + ".xlsx");
                BigExcelWriter writer = ExcelUtil.getBigWriter(file);

                //循环读取错误日志记录信息
                Set<String> keys = errorExcelMap.keySet();
                for(String key : keys){
                    //获取对应的错误信息
                    List<List<Object>> errorListForFile = errorExcelMap.get(key);
                    logger.error("key:{} size:{} ", key, errorListForFile.size());
                    if(errorListForFile.size() <= 2){
                        continue ;
                    }
                    //写入sheet页数据
                    if(errNum <= 0){
                        writer.renameSheet(key);
                    }else {
                        writer.setSheet(key);
                    }
                    errNum += errorListForFile.size() - 2;

                    CellStyle textStyle = writer.getWorkbook().createCellStyle();
                    DataFormat format = writer.getWorkbook().createDataFormat();
                    textStyle.setDataFormat(format.getFormat("@"));
                    for (int i = 0; i < errorListForFile.get(1).size(); i++) {
                        writer.setColumnWidth(i, 20);
                        writer.getSheet().setDefaultColumnStyle(i, textStyle);
                    }
                    writer.merge(errorListForFile.get(1).size() - 1, errorListForFile.remove(0).get(0));
                    writer.setDefaultRowHeight(20);
                    Cell cell = writer.getCell(0, 0);
                    CellStyle cellStyle = cell.getCellStyle();
                    cellStyle.setFillForegroundColor(IndexedColors.SKY_BLUE.getIndex());
                    cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
                    Font font = writer.createFont();
                    font.setBold(true);
                    font.setFontHeightInPoints((short) 16);
                    cellStyle.setFont(font);
                    cell.setCellStyle(cellStyle);
                    writer.write(errorListForFile);
                }
                // 一次性写出内容，使用默认样式
                writer.close();

                AdminMessage adminMessage = adminService.getMessageById(getUploadExcelBO().getMessageId()).getData();
                adminMessage.setTitle(String.valueOf(num.get() - sheetNum * 2));
                adminMessage.setContent(errNum + "," + updateNum.get());
                if (errNum > 0) {
                    MultipartFile multipartFile = MultipartFileUtil.getMultipartFile(file);
                    //上传OSS
                    UploadEntity uploadEntity = fileService.uploadTempFile(multipartFile, IdUtil.simpleUUID()).getData();
                    adminMessage.setTypeId(null);
                    //将错误信息的excel保存七天 604800 七天的秒数
                    redis.setex(AdminCacheKey.UPLOAD_EXCEL_MESSAGE_PREFIX + "file:" + adminMessage.getMessageId().toString(), 604800, uploadEntity.getPath());
                }else{
                    FileUtil.del(file.getAbsolutePath());
                }
                adminService.saveOrUpdateMessage(adminMessage);
                FileUtil.del(getUploadExcelBO().getFilePath());
            }catch (Exception ex){
                logger.error("ex:{}",ex, ex);
            }
        }

        /**
         * 写入错误日志信息
         * @param sheetName
         */
        public void writeErrLog(String sheetName){
            //将异常数据写入到临时的错误日志中
            List<List<Object>> errorLocalList = new CopyOnWriteArrayList<>();
            errorLocalList.addAll(errorList);
            errorExcelMap.put(sheetName, errorLocalList);
            errorList.clear();
        }
    }
    /**
     * 清理掉已存在相关用户信息
     * @param employeeId
     */
    public void clearEmployeeInfo(Long employeeId){
        logger.error("清理用户信息:{}", employeeId);
        //教育经历
        List<HrmEmployeeEducationExperience> educationExperienceList = ApplicationContextHolder.getBean(
                IHrmEmployeeEducationExperienceService.class).lambdaQuery().eq(HrmEmployeeEducationExperience::getEmployeeId, employeeId).list();
        educationExperienceList.forEach(hrmEmployeeEducationExperienceDel -> {
            ApplicationContextHolder.getBean(IHrmEmployeeEducationExperienceService.class).removeById(hrmEmployeeEducationExperienceDel.getEducationId());
        });

        //删除工作经历
        List<HrmEmployeeWorkExperience> workExperienceList = ApplicationContextHolder.getBean(IHrmEmployeeWorkExperienceService.class).lambdaQuery().eq(HrmEmployeeWorkExperience::getEmployeeId, employeeId).list();
        workExperienceList.forEach(hrmEmployeeWorkExperienceDel -> {
            ApplicationContextHolder.getBean(IHrmEmployeeWorkExperienceService.class).removeById(hrmEmployeeWorkExperienceDel.getWorkExpId());
        });

        //删除联系人
        List<HrmEmployeeContacts> contactsList = ApplicationContextHolder.getBean(IHrmEmployeeContactsService.class).lambdaQuery().eq(HrmEmployeeContacts::getEmployeeId, employeeId).list();
        contactsList.forEach(hrmEmployeeContactsDel -> {
            ApplicationContextHolder.getBean(IHrmEmployeeContactsService.class).removeById(hrmEmployeeContactsDel.getContactsId());
        });

        //删除员工主要社会关系
//        List<HrmEmployeeSocial> socialList = ApplicationContextHolder.getBean(IHrmEmployeeSocialService.class).lambdaQuery().eq(HrmEmployeeSocial::getEmployeeId, employeeId).list();
//        socialList.forEach(hrmEmployeeSocialDel -> {
//            ApplicationContextHolder.getBean(IHrmEmployeeSocialService.class).removeById(hrmEmployeeSocialDel.getSocialId());
//        });

        //删除专业技术职务
        List<HrmEmployeeTechnical> technicalList = ApplicationContextHolder.getBean(IHrmEmployeeTechnicalService.class).lambdaQuery().eq(HrmEmployeeTechnical::getEmployeeId, employeeId).list();
        technicalList.forEach(hrmEmployeeTechnicalDel -> {
            ApplicationContextHolder.getBean(IHrmEmployeeTechnicalService.class).removeById(hrmEmployeeTechnicalDel.getTechnicalId());
        });

        //删除职业资格
        List<HrmEmployeeCertificate> certificateList = ApplicationContextHolder.getBean(IHrmEmployeePostService.class).lambdaQuery().eq(HrmEmployeeCertificate::getEmployeeId, employeeId).list();
        certificateList.forEach(e -> {
            ApplicationContextHolder.getBean(IHrmEmployeePostService.class).removeById(e.getCertificateId());
        });

        //删除专业技术工人数据
        List<HrmEmployeeSpecialWorker> hrmEmployeeSpecialWorkerList =  ApplicationContextHolder.getBean(IHrmEmployeeSpecialWorkerService.class).lambdaQuery().eq(HrmEmployeeSpecialWorker::getEmployeeId, employeeId).list();
        hrmEmployeeSpecialWorkerList.forEach(e -> {
            ApplicationContextHolder.getBean(IHrmEmployeeSpecialWorkerService.class).removeById(e.getSpecialWorkerId());
        });

        //删除兼职信息
        List<HrmEmployeeConcurrentPost> employeesConcurrentPostList =  ApplicationContextHolder.getBean(IHrmEmployeeConcurrentPostService.class).lambdaQuery().eq(HrmEmployeeConcurrentPost::getEmployeeId, employeeId).list();
        employeesConcurrentPostList.forEach(e -> {
            ApplicationContextHolder.getBean(IHrmEmployeeConcurrentPostService.class).removeById(e.getConcurrentPostId());
        });

        //其他暂不处理
    }

    public abstract class UploadAttendanceService implements Runnable {

        protected List<List<Object>> errorList = new CopyOnWriteArrayList<>();

        protected volatile AtomicInteger num = new AtomicInteger(0);

        protected volatile AtomicInteger updateNum = new AtomicInteger(0);


        public abstract void importExcel();

        public abstract UploadExcelBO getUploadExcelBO();

        @Override
        public abstract void run();


    }

    public class EmployeeAttendanceUploadService extends UploadAttendanceService {

        private UploadExcelBO uploadExcelBO;

        private EmployeeAttendanceUploadService(UploadExcelBO uploadExcelBO) {
            this.uploadExcelBO = uploadExcelBO;
        }

        @Override
        public void importExcel() {
            ExcelUtil.readBySax(uploadExcelBO.getFilePath(), 0, (int sheetIndex, long rowIndex, List<Object> rowList) -> {
                int num = this.num.incrementAndGet();
                redis.setex(AdminCacheKey.UPLOAD_EXCEL_MESSAGE_PREFIX + getUploadExcelBO().getMessageId().toString(), UPLOAD_EXCEL_EXIST_TIME, Math.max(num - 2, 0));
                int maxIndex = 10001;
                if (this.num.get() > maxIndex) {
                    rowList.add(0, "最多同时导入10000条数据");
                    errorList.add(rowList);
                    return;
                }
                if (rowIndex > 1) {
                    int cardType = 3;
                    int source = 4;
                    int state = 5;
                    int stage = 6;
                    int cardAddress = 7;
                    int longitude = 8;
                    int latitude = 9;
                    try {
                        if (StrUtil.isEmptyIfStr(rowList.get(0))) {
                            rowList.add(0, "手机号不能为空");
                            errorList.add(rowList);
                            return;
                        }
                        if (StrUtil.isEmptyIfStr(rowList.get(1))) {
                            rowList.add(0, "上班日期时间不能为空");
                            errorList.add(rowList);
                            return;
                        }
                        if (StrUtil.isEmptyIfStr(rowList.get(cardType))) {
                            rowList.add(0, "打卡类型不能为空");
                            errorList.add(rowList);
                            return;
                        }
                        if (StrUtil.isEmptyIfStr(rowList.get(source))) {
                            rowList.add(0, "打卡来源为空");
                            errorList.add(rowList);
                            return;
                        }
                        if (StrUtil.isEmptyIfStr(rowList.get(state))) {
                            rowList.add(0, "打卡状态不能为空");
                            errorList.add(rowList);
                            return;
                        }
                        if (StrUtil.isEmptyIfStr(rowList.get(stage))) {
                            rowList.add(0, "打卡阶段不能为空");
                            errorList.add(rowList);
                            return;
                        }
                        if (StrUtil.isEmptyIfStr(rowList.get(cardAddress))) {
                            rowList.add(0, "打卡地址不能为空");
                            errorList.add(rowList);
                            return;
                        }
                        if (StrUtil.isEmptyIfStr(rowList.get(longitude))) {
                            rowList.add(0, "经度不能为空");
                            errorList.add(rowList);
                            return;
                        }
                        if (StrUtil.isEmptyIfStr(rowList.get(latitude))) {
                            rowList.add(0, "纬度不能为空");
                            errorList.add(rowList);
                            return;
                        }
                        String phone = rowList.get(0).toString().trim();
                        String clockTime = rowList.get(1).toString().trim();
                        String attendanceTime = rowList.get(2).toString().trim();
                        String clockType = rowList.get(3).toString().trim();
                        String type = rowList.get(4).toString().trim();
                        String clockStatus = rowList.get(5).toString().trim();
                        String clockStage = rowList.get(6).toString().trim();
                        String address = rowList.get(7).toString().trim();
                        String lng = rowList.get(8).toString().trim();
                        String lat = rowList.get(9).toString().trim();
                        String remark = rowList.get(10).toString().trim();
                        HrmAttendanceClock hrmAttendanceClock = new HrmAttendanceClock();
                        Optional<HrmEmployee> employeeOpt = ApplicationContextHolder.getBean(IHrmEmployeeService.class).lambdaQuery().select(HrmEmployee::getEmployeeId).eq(HrmEmployee::getMobile, phone).oneOpt();
                        if (!employeeOpt.isPresent()) {
                            rowList.add(0, "手机号对应的员工不存在");
                            errorList.add(rowList);
                            return;
                        } else {
                            hrmAttendanceClock.setClockEmployeeId(employeeOpt.get().getEmployeeId());
                        }
                        hrmAttendanceClock.setAttendanceTime(DateUtil.parseDateTime(attendanceTime).toLocalDateTime());
                        hrmAttendanceClock.setClockTime(DateUtil.parseDateTime(clockTime).toLocalDateTime());
                        hrmAttendanceClock.setClockType(ClockType.valueOfType(clockType));
                        hrmAttendanceClock.setClockStatus(ClockStatusEnum.valueOfType(clockStatus));
                        hrmAttendanceClock.setType(ClockType.valueOfType(type));
                        hrmAttendanceClock.setClockStage(Convert.toInt(clockStage));
                        hrmAttendanceClock.setAddress(address);
                        hrmAttendanceClock.setLng(lng);
                        hrmAttendanceClock.setLat(lat);
                        hrmAttendanceClock.setRemark(remark);
                        ApplicationContextHolder.getBean(IHrmAttendanceClockService.class).save(hrmAttendanceClock);
                    } catch (CrmException e) {
                        log.error("导入数据异常:", e);
                        rowList.add(0, e.getMsg());
                        errorList.add(rowList);
                    } catch (JSONException e) {
                        log.error("导入数据异常:", e);
                        rowList.add(0, "数据格式异常,请使用模板规定格式");
                        errorList.add(rowList);
                    } catch (Exception ex) {
                        log.error("导入数据异常:", ex);
                        rowList.add(0, "导入异常");
                        errorList.add(rowList);
                    }
                } else {
                    errorList.add(0, rowList);
                }
            });
        }

        @Override
        public UploadExcelBO getUploadExcelBO() {
            return uploadExcelBO;
        }

        @Override
        public void run() {
            boolean exists = redis.exists(AdminCacheKey.UPLOAD_EXCEL_MESSAGE_PREFIX + uploadExcelBO.getMessageId());
            if (!exists) {
                return;
            }
            String errorFilePath = null;
            AdminMessage adminMessage = adminService.getMessageById(getUploadExcelBO().getMessageId()).getData();

            try {
                UserUtil.setUser(getUploadExcelBO().getUserInfo());
                importExcel();

                adminMessage.setTitle(String.valueOf(num.get() - 2));
                adminMessage.setContent((errorList.size() - 2) + "," + updateNum.get());

                if (errorList.size() > TWO) {
                    File file = FileUtil.file("excelData/" + IdUtil.simpleUUID() + ".xlsx");
                    BigExcelWriter writer = ExcelUtil.getBigWriter(file);
                    CellStyle textStyle = writer.getWorkbook().createCellStyle();
                    DataFormat format = writer.getWorkbook().createDataFormat();
                    textStyle.setDataFormat(format.getFormat("@"));
                    for (int i = 0; i < errorList.get(1).size(); i++) {
                        writer.setColumnWidth(i, 20);
                        writer.getSheet().setDefaultColumnStyle(i, textStyle);
                    }
                    writer.merge(errorList.get(1).size() - 1, errorList.remove(0).get(0));
                    writer.setDefaultRowHeight(20);
                    Cell cell = writer.getCell(0, 0);
                    CellStyle cellStyle = cell.getCellStyle();
                    cellStyle.setFillForegroundColor(IndexedColors.SKY_BLUE.getIndex());
                    cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
                    Font font = writer.createFont();
                    font.setBold(true);
                    font.setFontHeightInPoints((short) 16);
                    cellStyle.setFont(font);
                    cell.setCellStyle(cellStyle);
                    // 一次性写出内容，使用默认样式
                    writer.write(errorList);
                    // 关闭writer，释放内存
                    writer.close();
                    adminMessage.setTypeId(null);
                    //将错误信息的excel保存七天 604800 七天的秒数

                    MultipartFile multipartFile = MultipartFileUtil.getMultipartFile(file);
                    //上传OSS
                    UploadEntity uploadEntity = fileService.uploadTempFile(multipartFile, IdUtil.simpleUUID()).getData();
                    errorFilePath = uploadEntity.getPath();

                    redis.setex(AdminCacheKey.UPLOAD_EXCEL_MESSAGE_PREFIX + "file:" + adminMessage.getMessageId().toString(), 604800, errorFilePath);
                }
                adminService.saveOrUpdateMessage(adminMessage);
            } catch (Exception e) {
                log.error("导入出现错误", e);
            } finally {
                redis.del(AdminCacheKey.UPLOAD_EXCEL_MESSAGE_PREFIX + uploadExcelBO.getMessageId());
                UserUtil.removeUser();
            }
            FileUtil.del(getUploadExcelBO().getFilePath());
        }
    }
}
